package jp.dodododo.dao.metadata;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import jp.dodododo.dao.annotation.Internal;
import jp.dodododo.dao.exception.SQLRuntimeException;
import jp.dodododo.dao.util.CaseInsensitiveMap;
import jp.dodododo.dao.util.ConnectionUtil;
import jp.dodododo.dao.util.DataSourceUtil;
import jp.dodododo.dao.util.ResultSetUtil;

@Internal
public class TableMetaData {
	private String tableName;

	private CaseInsensitiveMap<ColumnMetaData> columnMetaData = new CaseInsensitiveMap<ColumnMetaData>();

	private CaseInsensitiveMap<ColumnMetaData> pkColumnMetaData = new CaseInsensitiveMap<ColumnMetaData>();

	public TableMetaData(Connection connection, String tableName) {
		init(connection, tableName);
	}

	public TableMetaData(DataSource dataSource, String tableName) {
		Connection connection = null;
		try {
			connection = DataSourceUtil.getConnection(dataSource);
			init(connection, tableName);
		} finally {
			ConnectionUtil.close(connection);
		}
	}

	private void init(Connection connection, String tableName) {
		try {
			this.tableName = tableName.toUpperCase();
			DatabaseMetaData metaData = connection.getMetaData();

			setUpColumnMetaData(metaData);
			setUpPks(metaData);
		} catch (SQLException e) {
			throw new SQLRuntimeException(e);
		}
	}

	private void setUpPks(DatabaseMetaData metaData) throws SQLException {
		String catalog = null;
		String schemaPattern = null;
		ResultSet primaryKeys = null;
		try {
			primaryKeys = metaData.getPrimaryKeys(catalog, schemaPattern, this.tableName);
			while (primaryKeys.next()) {
				String columnName = primaryKeys.getString(4);
				ColumnMetaData columnMetaData = getColumnMetaData(columnName);
				columnMetaData.setPrimaryKey(true);
				pkColumnMetaData.put(columnName, columnMetaData);
			}
		} finally {
			ResultSetUtil.close(primaryKeys);
		}
	}

	private void setUpColumnMetaData(DatabaseMetaData metaData) {
		ResultSet columns = null;
		try {
			String catalog = null;
			String schemaPattern = null;
			String tableNamePattern = this.tableName;
			String columnNamePattern = null;
			columns = metaData.getColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
			while (columns.next()) {
				ColumnMetaData columnMetaData = new ColumnMetaData();
				columnMetaData.setTableCat(columns.getString(1));
				columnMetaData.setTableSchem(columns.getString(2));
				columnMetaData.setTableName(columns.getString(3));
				columnMetaData.setColumnName(columns.getString(4));
				columnMetaData.setDataType(columns.getInt(5));
				columnMetaData.setTypeName(columns.getString(6));
				columnMetaData.setColumnSize(columns.getInt(7));
				columnMetaData.setBufferLength(columns.getInt(8));
				columnMetaData.setDecimalDigits(columns.getInt(9));
				columnMetaData.setNumPrecRadix(columns.getInt(10));
				columnMetaData.setNullable(columns.getInt(11));
				columnMetaData.setRemarks(columns.getString(12));
				columnMetaData.setColumnDef(columns.getString(13));
				columnMetaData.setSqlDataType(columns.getString(14));
				columnMetaData.setSqlDatetimeSub(columns.getInt(15));
				columnMetaData.setCharOctetLength(columns.getInt(16));
				columnMetaData.setOrdinalPosition(columns.getInt(17));
				columnMetaData.setIsNullable(columns.getString(18));
				columnMetaData.setScopeCatlog(columns.getString(19));
				columnMetaData.setScopeSchema(columns.getString(20));
				columnMetaData.setScopeTable(columns.getString(21));
				columnMetaData.setSqlDataType(columns.getString(22));
				columnMetaData.setIsAutoincrement(columns.getString(23));
				this.columnMetaData.put(columnMetaData.getColumnName(), columnMetaData);
			}
		} catch (SQLException e) {
			throw new SQLRuntimeException(e);
		} finally {
			ResultSetUtil.close(columns);
		}
	}

	public String getTableName() {
		return tableName;
	}

	public List<String> getColumnNames() {
		List<String> columnNames = new ArrayList<String>();
		columnNames.addAll(columnMetaData.keySet());
		return columnNames;
	}

	public List<String> getPkColumnNames() {
		List<String> columnNames = new ArrayList<String>();
		columnNames.addAll(pkColumnMetaData.keySet());
		return columnNames;
	}

	public ColumnMetaData getColumnMetaData(String columnName) {
		return this.columnMetaData.get(columnName);
	}
}
